/** @俄罗斯方块 */
// 数据引入区
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
import React, { useEffect, useRef, useState } from 'react'
import './Tetris.less' // 首页样式
import './switchTetris.less' // 首页样式

// 俄罗斯方块公共数据
// ------------------------------------------------------------------------------
import { exhibitionItemObj } from './ts/tetrisItemData' // 导入提示屏幕成员展示原始数据对象

// 俄罗斯方块公共api
// ------------------------------------------------------------------------------
import {
  initializationTetrisItem, // 俄罗斯方块成员初始化函数
  tetrisItemSwitchState, // 俄罗斯方块状态切换方法
  tetrisItemDisplacement, //俄罗斯方块位移函数
} from './ts/tetrisItem'

// 公共方法
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
import { EstablishTimer } from '../../utils/index' // 导入计时器类方法

// ts接口
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
import {
  RefReturn,
  displayListItem,
  MonitorUpdate,
} from './components/monitor/interface'

import {
  InitializationTetrisItemReturn,
  TetrisItemObjItemstateListTypeConst,
} from './interface/tetrisItemInterface'

import { tetrisTipsControl } from './interface/index'

// 组件引入
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
import Monitor from './components/monitor/monitor' // 显示器组件
import LightweightButton from './components/lightweightButton/lightweightButton' // 轻量级按键组件

// 页面书写区
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
const Tetris: React.FC = () => {
  // 变量声明区
  // ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // ref声明
  const gameMonitorRef = useRef<RefReturn>(null) // 游戏屏幕Ref
  const gameMonitorListPreprocessList = useRef<displayListItem>([]) // 游戏屏幕预处理数组
  const gameUserOperation = useRef<InitializationTetrisItemReturn>(null) // 用户当前操作的方块内容对象
  const gameShowPointsList = useRef<displayListItem>(null) // 改变后的显示点承接集合数组
  const gameNewestShowList = useRef<Array<displayListItem>>(null) // 最新屏幕展示数据承接数组
  const gameMonitorRefreshTimer = useRef<NodeJS.Timeout>(null) // 游戏刷新定时器
  const gameMonitorRefreshTime = useRef<number>(1500) // 游戏刷新定时器计时时间
  const accelerateTimer = useRef<NodeJS.Timeout>(null) // 加速按键定时器
  const accelerateTime = useRef<number>(330) // 加速按键定时器计时时间
  const timerClass = useRef(null) // 计时器类定义
  const backAudio = useRef(null) // 计时器类定义

  // 方块提示区域
  // ================================================================
  const blockTipsShow = useRef<InitializationTetrisItemReturn>(null) // 方块提示区域当前展示内容对象

  // 状态声明
  // ------------------------------------------------------------------------------
  // 游戏显示器
  const [gameMonitorList, setGameMonitorList] = useState<displayListItem>([]) // 游戏屏幕渲染数组
  const [gameDownNumber, setGameDownNumber] = useState<number>(1) // 游戏展示下移变化值

  // 方块提示区域
  // ================================================================
  const [blockTips, setBlockTips] = useState<displayListItem>([]) // 方块提示区屏幕渲染数组屏幕渲染数组
  const [TipsControl, setTipsControls] = useState<tetrisTipsControl>(null) // 方块提示区屏幕行列控制对象

  // 游戏功能按键区
  // ================================================================
  const [openReopen, setOpenReopen] = useState<boolean>(false) // 重开按键
  const [startGameState, setStartGameState] = useState<boolean>(false) // 游戏开始按键
  const [openMusic, setOpenMusic] = useState<boolean>(false) // 音乐按键
  const [skinPeeler, setSkinPeeler] = useState<boolean>(false) // 换肤按键

  // 显示分数等内容区
  // ================================================================
  const [gameDuration, setGameDuration] = useState<string>('0秒') // 当局游戏进行时长
  const [currentScore, setCurrentScore] = useState<number>(0) // 当前分数
  const [highestScore, setHighestScore] = useState<number>(0) // 最高分数
  const [difficulty, setDifficulty] = useState<number>(0) // 难度
  const [eliminate, setEliminate] = useState<number>(0) // 清除

  // 函数使用区
  // ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  // hook函数
  // ------------------------------------------------------------------------------
  // 对于重开进行监听
  useEffect(() => {
    // 如果启动了重开状态
    if (openReopen) {
      // 停止定时器为了防错在此再执行一次
      addOrdDeleteTimer('delete')

      // 开始按键状态发生变化
      setStartGameState(false)

      // 判断最高分数是否小于当前分数，如果是则对最高分数进行更新
      highestScore < currentScore && setHighestScore(currentScore)

      // 重置游戏定时器的时间
      gameMonitorRefreshTime.current = 1500

      // 重置计时器类
      timerClass.current.again()
      console.log('gameOver')

      // 如果关闭了重开状态
    } else {
      // 执行提示屏幕初始化函数
      blockTipsInit()

      // 首次加载时给 计时器类ref进行绑定
      blockTips.length === 0 &&
        (timerClass.current = new EstablishTimer(singleRoundTime))

      // 给音乐播放器进行绑定音乐资源
      blockTips.length === 0 &&
        (backAudio.current.src = require('./music/backMusic.mp3'))

      // 如果是首次加载时则不用进行以下操作
      if (blockTips.length !== 0) {
        setCurrentScore(0) // 初始化当前分数
        setStartGameState(true) // 打开游戏开始按键
        setGameMonitorList([]) // 初始化游戏屏幕的渲染数组
        setGameDuration('0秒') // 初始化当局游戏时长展示数据
        setDifficulty(0) // 初始化难度值
        setEliminate(0) // 初始化清除值

        // 开启定时器，在初始后在进行重置状态（如果不用定时器，则适合上方的state一起改变达不到需要的效果）
        setTimeout(() => {
          switchInit()
        }, 300)

        // 添加全局下移的定时器
        addOrdDeleteTimer('add')
        // 重置计时器类
        timerClass.current.open()
      }
    }
  }, [openReopen])

  // 对于当前分数进行监听
  // ================================================================
  useEffect(() => {
    // 如果现在进行时间值已经低于或者等于加速按键的时间值了，则直接弹出
    if (gameMonitorRefreshTime.current <= accelerateTime.current) {
      return
    }

    // 声明 难度阶梯 difficultyLadder + 当局时间变量 authorityTime
    let difficultyLadder: number, authorityTime: number
    // 难度阶梯按照分数的50的倍数进行显示
    difficultyLadder = Math.trunc(currentScore / 50)
    // 进度的时间按照 difficultyLadder 的20倍进行减少
    authorityTime = gameMonitorRefreshTime.current - difficultyLadder * 20
    // 如果低于了加速按键的时效，为了防错直接赋值到加速的按键的值
    authorityTime < accelerateTime.current &&
      (authorityTime = accelerateTime.current)

    // 同步时间+难度阶梯数
    gameMonitorRefreshTime.current = authorityTime
    setDifficulty(difficultyLadder)
  }, [currentScore])

  // 对于音乐状态进行监听
  // ================================================================
  useEffect(() => {
    // 根据音乐状态执行打开还是关闭音乐播放
    openMusic ? backAudio.current.play() : backAudio.current.pause()
  }, [openMusic])

  // 对于下移动数值进行监听，发生变化则进行对应的位移操作
  // ================================================================
  useEffect(() => {
    // 防止首次加载时，触发了初始化赋值，导致没有数据而做的防错
    if (!gameUserOperation?.current) {
      return
    }

    // 此时变化值是会产生变化的，下移每次都是1个
    userChangeGameMonitor(1, 0)
  }, [gameDownNumber])

  // 事件函数
  // ------------------------------------------------------------------------------
  // 切换初始化函数
  const switchInit = () => {
    // 对 游戏屏幕预处理数组 进行和 改变后的显示点承接集合数组同步
    gameMonitorListPreprocessList.current = gameShowPointsList.current

    // 对 用户当前操作的方块内容对象 进行赋值（继承 当时的提示屏幕的内容）
    gameUserOperation.current = blockTipsShow.current

    // 从游戏屏幕组件中结构出需要的参数+从当前操作的对象里结构出需要的参数
    const { transverse } = gameMonitorRef.current

    // 对于原点的列数初始化计算，
    let randTran =
      // 总列数-1得到有效的数组位置信息最大值
      (transverse -
        1 -
        // 取当前的游戏操作对象中最右边的任意一个成员的列数,得到当前该成员最长的位置信息
        Math.abs(gameUserOperation.current.downSpot.length)) /
      // 两者的差值除以2,得到真实的原点列真实位置
      2

    // 将底部显示数组结构出来
    let { downSpot } = gameUserOperation.current
    // 声明一个变量作为是为重开的依据
    let isReopen = false
    // 是否进行重开的变量声明,当前操作对象的最下方的数组带入循环,只要里面有一个是满足创建后的位置上有点已经显示了,则是true
    isReopen = downSpot.some((value) => {
      // 从最新的渲染数组中进行获取是否有点和下一个生成的点位有重合的地方
      let states = gameMonitorListPreprocessList?.current?.some((val) => {
        return (
          val.portrait === 0 &&
          val.transverse === value.transverse + Math.ceil(randTran) - 1
        )
      })

      return states
    })

    // 如果重开变量变成了true,那么直接中断操作,进行游戏结束的逻辑
    if (isReopen) {
      // 改变重开状态
      setOpenReopen(true)

      return
    }

    //只要当不用重开时,才对游戏屏幕用户操作变化函数
    userChangeGameMonitor(
      0 - gameUserOperation.current.originList[0],
      Math.ceil(randTran) - 1
    )

    // 使用提示屏幕初始化内容函数
    blockTipsInit()
  }

  // 游戏屏幕用户操作变化函数
  // porChange列改变值
  // tranChange纵改变值
  // ================================================================
  const userChangeGameMonitor = (porChange: number, tranChange: number) => {
    // 对 本来的初始化数据 进行位移
    gameUserOperation.current = tetrisItemDisplacement(
      porChange,
      tranChange,
      gameUserOperation.current
    )

    // 声明一个变量承接完整游戏屏幕渲染数组进行变化
    let gameMonitorChangeList = [
      ...gameMonitorListPreprocessList.current,
      ...gameUserOperation.current.allSpot,
    ]

    // 改变游戏显示屏幕函数
    setGameMonitorList(gameMonitorChangeList as unknown as displayListItem)
  }

  // 游戏屏幕用户操作状态替换函数
  // porChange列改变值
  // tranChange纵改变值
  // ================================================================
  const userChangeGameMonitorState = (list) => {
    // 声明一个变量承接完整游戏屏幕渲染数组进行变化
    let gameMonitorChangeList = [
      ...gameMonitorListPreprocessList.current,
      ...list.allSpot,
    ]

    // 改变游戏显示屏幕函数
    setGameMonitorList(gameMonitorChangeList as unknown as displayListItem)
  }

  // 提示屏幕初始化内容函数
  // ================================================================
  const blockTipsInit = () => {
    // 先对 俄罗斯方块数据 进行初始化
    let blockTipsShowInit = initializationTetrisItem()

    // 然后对其进行结构，得到需要的参数方便后续使用
    let { name, stateName } = blockTipsShowInit

    // 在提示屏幕成员展示原始数据对象中找到对应的内容，然后将其结构出来
    let {
      portrait,
      transverse,
      port,
      tran,
    }: TetrisItemObjItemstateListTypeConst = exhibitionItemObj[name][stateName]

    // 使用原点改变方法，找到对应的值，对提示显示器进行原点适配+行列更换,和方块提示区域当前展示内容对象赋值
    blockTipsShow.current = tetrisItemDisplacement(
      portrait,
      transverse,
      blockTipsShowInit
    )

    // 进行改变 方块提示区屏幕渲染数组屏幕渲染数组 + 方块提示区屏幕行列控制对象
    setBlockTips(blockTipsShow.current.allSpot as unknown as displayListItem)
    setTipsControls({ port, tran })
  }

  // 游戏屏幕更新后的回调函数
  // ================================================================
  const gameMonitorUpdate: MonitorUpdate = ({
    showPointsList,
    newestShowList,
    monitorState,
  }) => {
    // 判断是否进行删除行的操作,并弹出
    if (monitorState === 'deletePortraits') {
      // 是的话，先进行同步修改实现修改的内容进行在游戏屏幕中展示
      setGameMonitorList(gameShowPointsList.current)

      // 设置定时器，优化用户体验
      setTimeout(() => {
        // 进行替换初始化
        switchInit()
        addOrdDeleteTimer('add')
      }, 1000)

      return
    }
    gameShowPointsList.current = showPointsList // 对 改变后的显示点承接集合数组 同步值
    gameNewestShowList.current = newestShowList // 对 最新屏幕展示数据承接数组 同步值
  }

  // 当前分数计算函数
  // ================================================================
  const scoreCalculation = (length: number) => {
    // 接收到传入的消除数组的长度然后按照比例更新当前分数
    let nowFraction = length * 10
    // 将之前分数进行相加并改变当前的分数
    setCurrentScore(currentScore + nowFraction)
  }

  // 重开按键
  // ================================================================
  const openReopenBtn = () => {
    // 先判断是否已经开启了重开状态
    if (openReopen) {
      // 是的话直接关闭
      setOpenReopen(false)
    } else {
      // 不是的话先改为true
      setOpenReopen(true)
      // 然后用异步的方法进行改变状态为false
      setTimeout(() => {
        setOpenReopen(false)
      })
    }
  }

  // 开始游戏按键
  // ================================================================
  const startGame = () => {
    // 判断是否是重开的状态，是的话，直接弹出
    if (openReopen) {
      return
    }

    // 先给游戏开始状态取反值
    let startGameStateContrary = !startGameState

    // 如果取反后为开启状态，表示此游戏要进行启动
    if (startGameStateContrary) {
      // 然后进行判断是否是首次启动，即游戏屏幕渲染数组长度为空
      if (!gameMonitorList.length) {
        // 是首次渲染的话，调用初始化切换函数
        switchInit()
      }
      // 调用下移定时器清除函数
      addOrdDeleteTimer('add')

      // 调用定时器类的打开函数
      timerClass.current.open()
    } else {
      // 调用下移定时器清除函数
      addOrdDeleteTimer('delete')

      // 调用定时器类的关闭函数
      timerClass.current.end()
    }

    // 改变游戏按键展示的状态
    setStartGameState(startGameStateContrary)
  }

  // 时间类的传入回调方法
  // ================================================================
  const singleRoundTime = (timeObj) => {
    // 从参数中结构出对应的数据
    const { day, hour, minute, second } = timeObj

    // 生成字符串用于展示
    let needText = `${day ? day + '天' : ''}${hour ? hour + '小时' : ''}${
      minute ? minute + '分' : ''
    }${second ? second + '秒' : ''}`

    // 修改当前分数的值
    setGameDuration(needText)
  }

  // 切换状态（方向键上）按键
  // ================================================================
  const stateSwitching = () => {
    // 如果没有进行游戏则直接弹出
    if (!startGameState) {
      return
    }

    // 改变状态函数
    changeState()
  }

  // 向下按键
  // ================================================================
  const moveDown = () => {
    // 如果没有进行游戏则直接弹出
    if (!startGameState) {
      return
    }

    // 清除定时器
    addOrdDeleteTimer('delete')

    // 执行游戏下移改变函数
    changeGameDownNumber()

    // 恢复定时器
    addOrdDeleteTimer('add')
  }

  // 左右移动按键
  // ================================================================
  const moveRight = (index: number) => {
    // 如果没有进行游戏则直接弹出
    if (!startGameState) {
      return
    }

    // 游戏左右移动改变函数的返回值确定是否可以进行左右移动
    changeGameLeftOrRight(index) && userChangeGameMonitor(0, index)
  }

  // 加速按键
  // ================================================================
  const accelerate = (type: string) => {
    // 如果没有进行游戏则直接弹出
    if (!startGameState) {
      return
    }

    // 清理内部的定时器
    clearInterval(accelerateTimer.current)
    // 清理内部的定时器的内存
    accelerateTimer.current = null

    switch (type) {
      // 开启加速按键时
      case 'open':
        // 先停止之前的定时器
        addOrdDeleteTimer('delete')
        // 然后创建定时器
        accelerateTimer.current = setInterval(() => {
          // 调用 游戏下移改变函数
          changeGameDownNumber()
        }, accelerateTime.current)
        break

      case 'end':
        // 开启全局的定时器
        addOrdDeleteTimer('add')
        break

      default:
        break
    }
  }

  // 定时器添加与消除函数
  // ================================================================
  const addOrdDeleteTimer = (change: string) => {
    // 当存在游戏定时器的时候，并调用了定时器的清理字段下对此时游戏移动的定时器进行清理
    clearInterval(gameMonitorRefreshTimer.current)
    switch (change) {
      case 'delete':
        // 清理定时器内存
        gameMonitorRefreshTimer.current = null
        break

      case 'add':
        // 赋予系统执行的定时器按时间操作进行改变下移状态
        gameMonitorRefreshTimer.current = setInterval(() => {
          changeGameDownNumber()
        }, gameMonitorRefreshTime.current)
        break

      default:
        break
    }
  }

  // 游戏下移改变函数
  // ================================================================
  const changeGameDownNumber = () => {
    // 首先对当前用户控制的数据对象进行结构，拿出来当前最下方的数据数组+所有数据数组
    let { downSpot, allSpot } = gameUserOperation.current
    // 然后将筛选的api+总纵数从游戏屏幕组件中结构出来
    const {
      appointPointStatusQuery,
      showRanksStatusQuery,
      portrait,
      transverse,
      deletePortraitTransverse,
    } = gameMonitorRef.current
    // 声明结果承接的数据,默认为false
    let isMoveDown: boolean = false

    // 判断当下移时，在此状态的下方展示数组的成员列数都是负数则表示已经结束游戏了
    if (downSpot[0].portrait + 1 < 0) {
      // 修改重开状态
      setOpenReopen(true)
      return
    }

    // 先从下方集合数组中拿出第一位的列数进行判断，如果这个值是小于总列数了则进行后续循环判断
    downSpot[0].portrait + 1 < portrait &&
      // 对 downSpot 进行使用every方法
      (isMoveDown = downSpot.every((value) => {
        // 将每个成员的位置写入的参数进行带入 appointPointStatusQuery 方法中进行获得下方的位置信息
        // 如果成员下面的点都是空，即都是false，则赋予 isMoveDown 为true
        return !appointPointStatusQuery({
          portrait: value.portrait + 1,
          transverse: value.transverse,
        })
      }))

    // 当 isMoveDown 为true时，即表示下方空白可以下移
    if (isMoveDown) {
      setTimeout(() => {
        // 当可以移动时改变下移的数值(此种写法是为了解决不能在定时器中生效的问题)
        setGameDownNumber((gameDownNumber) => gameDownNumber + 1)
      })

      // 当为false时，即表示下方有值，不能下移
    } else {
      addOrdDeleteTimer('delete')

      // 首先对此成员所有成员进行查看，是否是存着已经在列上超过了屏幕了
      let isReopen = allSpot.some((value) => {
        return value.portrait < 0
      })

      // 如果有这个值存在，则直接gameOver
      if (isReopen) {
        setOpenReopen(true)
        return
      }

      // 执行查询显示的纵和列的查询函数,并结构处已经筛选好了的数据内容
      let queryList = showRanksStatusQuery() as number[]

      // 判断是否有满纵的情况
      if (queryList.length) {
        // 对查询到的列数进行带入 gameShowPointsList 即当前最新点展示情况 进行定点过滤,得到筛选后的原始数据数组
        queryList.forEach((value) => {
          // gameShowPointsList 过滤+赋值，将 queryList 查询到的列数进行找到并过滤掉，得到剩下的数据
          gameShowPointsList.current = gameShowPointsList.current.filter(
            (val) => {
              return val.portrait !== value
            }
          )
        })

        // 将过滤后的数据进行在此二次处理，即依次将 删除后的列数上面的方块集体下移
        queryList.forEach((value) => {
          gameShowPointsList.current = gameShowPointsList.current.map((val) => {
            val.portrait < value && (val.portrait += 1)
            return val
          })
        })

        // 如果有则进行执行消除和再次循环函数
        deletePortraitTransverse({ portrait: queryList })

        // 更新当前的分数值
        scoreCalculation(queryList.length)

        // 更新消除的数值
        setEliminate(eliminate + queryList.length * transverse)

        // 如果都没有，则表示用户还可以进行游戏，则执行 切换初始化函数
      } else {
        addOrdDeleteTimer('delete')
        switchInit()
        addOrdDeleteTimer('add')
      }
    }
  }

  // 游戏左右移动改变函数
  // ================================================================
  const changeGameLeftOrRight = (num: number) => {
    // 首先对当前用户控制的数据对象进行结构，拿出来当前最下方的数据数组+所有数据数组
    let { rightSpot, leftSpot } = gameUserOperation.current
    // 然后将筛选的api+总纵数从游戏屏幕组件中结构出来
    const { appointPointStatusQuery, transverse } = gameMonitorRef.current

    // 声明一个承接变化的数组
    let changeList = num > 0 ? rightSpot : leftSpot

    // 声明一个是否移动的变量
    let isMove = !changeList.some((value) => {
      // 判断此移动后的值是否在屏幕外，是的话直接弹出
      if (value.transverse + num >= transverse || value.transverse + num < 0) {
        return true
      }

      // 如果在屏幕内，则进行判断移动后的值是否已经被点亮了，如果是也弹出
      return appointPointStatusQuery({
        portrait: value.portrait,
        transverse: value.transverse + num,
      })
    })

    // 将处理后的参数进行返回
    return isMove
  }

  // 改变当前控制方块状态函数
  // ================================================================
  const changeState = () => {
    // 如果是田字型的方块不用执行以下方法，直接弹出
    if (gameUserOperation.current.stateListLength === 1) {
      return
    }
    // 使用改变方块状态的方法
    let changeItem = tetrisItemSwitchState(gameUserOperation.current)

    // 从新的值里得到数据进行结构
    let { allSpot } = changeItem

    // 右边边缘屏幕的超出值
    let modifyDifference: number = 0

    // 对当前的所以成员数组进行遍历
    allSpot.forEach((value) => {
      // 如果有成员超过了屏幕显示的列
      if (value.transverse > gameMonitorRef.current.transverse - 1) {
        // 则对边缘超出值进行赋值（取值当前列数-总列数的差+1）
        modifyDifference +=
          value.transverse - gameMonitorRef.current.transverse + 1
      }
    })

    // 然后调用方块移动函数，传入对应差值（注意应该为负数）
    changeItem = tetrisItemDisplacement(0, -modifyDifference, changeItem)

    // 声明变量进行承接到变更后的点是否有位置变化
    let isChange = !changeItem.allSpot.some((value) => {
      if (value.transverse < 0) {
        return true
      }

      // 如果在现有的展示内存里进行寻找，是否有位置可以变更
      return gameMonitorListPreprocessList.current.some((val) => {
        return (
          val.transverse === value.transverse && val.portrait === value.portrait
        )
      })
    })

    // 如果有则对其进行更新位置状态信息
    if (isChange) {
      addOrdDeleteTimer('delete')
      gameUserOperation.current = changeItem as InitializationTetrisItemReturn

      userChangeGameMonitorState(gameUserOperation.current)
      addOrdDeleteTimer('add')
    }
  }

  // dom元素区域
  // ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
  return (
    <div className={`${skinPeeler ? 'switchTetris' : 'Tetris'}`}>
      {/* 计时器 */}
      <div className="Tetris-time">时长：{gameDuration}</div>
      {/* 当前分数展示 */}
      {/* ------------------------------------------------------------------------------ */}
      <div className="Tetris-fraction">{currentScore}</div>
      {/* 内容展示 */}
      {/* ------------------------------------------------------------------------------ */}
      <div className="Tetris-content">
        {/* 左边展示区 */}
        {/* ================================================================ */}
        <div className="Tetris-content-left">
          {/* 分数展示 */}
          <div className="Tetris-content-left-fraction">
            <p>最高分数</p>
            {/* 分数展示 */}
            <span>{highestScore}</span>
          </div>

          {/* 音乐按键 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <LightweightButton
            buttonCSS={`Tetris-content-left-music ${
              openMusic
                ? 'Tetris-content-left-voice'
                : 'Tetris-content-left-Mute'
            }`}
            changeBackgroundColor={
              !skinPeeler ? 'rgba(223,225,233,0.7)' : 'rgba(28,29,35,0.7)'
            }
            isAntiShake
            userClick={() => {
              // 控制音乐开关
              setOpenMusic(!openMusic)
            }}
          />

          {/* 背景颜色更换按键 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <LightweightButton
            buttonCSS="Tetris-content-left-switchBackgroundColor"
            changeBackgroundColor={
              !skinPeeler ? 'rgba(223,225,233,0.7)' : 'rgba(28,29,35,0.7)'
            }
            isAntiShake
            userClick={() => {
              // 控制换肤开关
              setSkinPeeler(!skinPeeler)
            }}
          />

          {/* 重开按键 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <LightweightButton
            buttonCSS="Tetris-content-left-reopen"
            changeBackgroundColor={
              !skinPeeler ? 'rgba(223,225,233,0.7)' : 'rgba(28,29,35,0.7)'
            }
            isAntiShake
            userClick={openReopenBtn}
          />
        </div>

        {/* 游戏显示器 */}
        {/* ================================================================ */}
        <Monitor
          monitorClass="MonitorCss"
          showPointsClass="MonitorItemCss"
          showPointsColor={skinPeeler ? '#101012' : '#fff'}
          displayList={gameMonitorList}
          ref={gameMonitorRef}
          monitorUpdate={gameMonitorUpdate}
        />

        {/* 右边展示区 */}
        {/* ================================================================ */}
        <div className="Tetris-content-right">
          {/* 提示显示器 */}
          <Monitor
            monitorClass="Tetris-content-right-Monitor"
            showPointsClass="Tetris-content-rightMonitorItem"
            showPointsColor={skinPeeler ? '#101012' : '#fff'}
            portrait={TipsControl?.port}
            transverse={TipsControl?.tran}
            displayList={blockTips}
          />

          {/* 难度显示 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <div className="Tetris-content-right-difficulty">
            <p>难度</p>
            <span>{difficulty}</span>
          </div>

          {/* 已消除展示 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <div className="Tetris-content-right-eliminated">
            <p>清除</p>
            <span>{eliminate}</span>
          </div>

          {/* 游戏开始暂停按键 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <LightweightButton
            buttonCSS={`Tetris-content-right-start ${
              startGameState
                ? 'Tetris-content-right-suspend'
                : 'Tetris-content-right-play'
            }`}
            changeBackgroundColor={
              !skinPeeler ? 'rgba(223,225,233,0.7)' : 'rgba(28,29,35,0.7)'
            }
            isAntiShake
            userClick={startGame}
          />
        </div>
      </div>
      {/* 按键操作 */}
      {/* ------------------------------------------------------------------------------ */}
      <div className="Tetris-operation">
        {/* 下移按键(加速按键) */}
        <LightweightButton
          buttonCSS="Tetris-operation-goDown"
          ifLongPress
          longPressTime={300}
          userClick={() => {
            accelerate('open')
          }}
          longPressEnd={() => {
            accelerate('end')
          }}
        />

        {/* 方向按键 */}
        {/* ================================================================ */}
        <div className="Tetris-operation-direction">
          {/* 向上按键 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <LightweightButton
            buttonCSS="Tetris-operation-direction-top"
            changeBackgroundColor={
              !skinPeeler ? 'rgba(223,225,233,0.7)' : 'rgba(28,29,35,0.7)'
            }
            isAntiShake
            userClick={stateSwitching}
          />

          {/* 向左按键 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <LightweightButton
            buttonCSS="Tetris-operation-direction-left"
            changeBackgroundColor={
              !skinPeeler ? 'rgba(223,225,233,0.7)' : 'rgba(28,29,35,0.7)'
            }
            isAntiShake
            userClick={() => {
              moveRight(-1)
            }}
          />

          {/* 向下按键 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <LightweightButton
            buttonCSS="Tetris-operation-direction-down"
            changeBackgroundColor={
              !skinPeeler ? 'rgba(223,225,233,0.7)' : 'rgba(28,29,35,0.7)'
            }
            isAntiShake
            userClick={moveDown}
          />

          {/* 向右按键 */}
          {/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */}
          <LightweightButton
            buttonCSS="Tetris-operation-direction-right"
            changeBackgroundColor={
              !skinPeeler ? 'rgba(223,225,233,0.7)' : 'rgba(28,29,35,0.7)'
            }
            isAntiShake
            userClick={() => {
              moveRight(1)
            }}
          />
        </div>
      </div>
      {/* 背景音频 */}
      {/* ------------------------------------------------------------------------------ */}
      <audio ref={backAudio} loop></audio>
    </div>
  )
}

// 导出首页
export default React.memo(Tetris)
